#include "VTime.h"
#include "Common.h"
#include "Worker.h"
#include "AStar.h"
#include "Map.h"
#include "Object.h"

Map* map;

Worker* AddNewWorker(const char* name, int type, COORD position)
{
    Worker* worker;

    workerCount++;
    workerList = (Worker**)realloc(workerList, workerCount * sizeof(Worker*));
    worker = (Worker*)malloc(sizeof(Worker));

    worker->name = (char*)calloc(strlen(name) + 1, sizeof(char)); strcpy(worker->name, name);
    worker->position = position;
    worker->destiny = position;
    worker->type = type;
    worker->dir = DIR_DEFAULT;
    worker->moving = false;
    worker->curTask = NULL;
    worker->workingObject = NULL;

    workerList[workerCount - 1] = worker;
    map->state[position.Y * map->width + position.X] = MAP_STATE_WORKER;
	
	return worker;
}


void MoveWorkers()
{
    int i;

    for (i = 0; i < workerCount; i++)
    {
        Worker* worker = workerList[i];
        int j = worker->position.Y * map->width + worker->position.X;

        if (worker->moving)
        {
            int elapsedTime = GetVirtualTime() - worker->lastDep;

            if (elapsedTime > TIME_PER_CASE)
            {
                map->state[j] = MAP_STATE_FREE;
                switch (worker->dir)
                {
                    case DIR_LEFT:  worker->position.X--; break;
                    case DIR_UP:    worker->position.Y--; break;
                    case DIR_RIGHT: worker->position.X++; break;
                    case DIR_DOWN:  worker->position.Y++; break;
                }

                j = worker->position.Y * map->width + worker->position.X;
                map->state[j] = MAP_STATE_WORKER;
                worker->moving = false;
            }
        }

        if (!worker->moving && !IsCoordEqual(worker->position, worker->destiny))
        {
            int elapsedTime = GetVirtualTime() - worker->lastDep;
            int newDir = GetNextDirection(worker->position.X, worker->position.Y, worker->destiny.X, worker->destiny.Y);

            if (newDir >= 0)
            {
                map->state[j] = MAP_STATE_MOVE;
                switch (newDir)
                {
                    case DIR_LEFT:  map->state[j - 1] = MAP_STATE_MOVE; break;
                    case DIR_UP:    map->state[j - map->width] = MAP_STATE_MOVE; break;
                    case DIR_RIGHT: map->state[j + 1] = MAP_STATE_MOVE; break;
                    case DIR_DOWN:  map->state[j + map->width] = MAP_STATE_MOVE; break;
                }
                worker->dir = newDir;
                worker->moving = true;
                worker->lastDep = GetVirtualTime();
            }
            else if (newDir == -2)
            {
                worker->destiny = worker->position;
                worker->moving = false;
                worker->lastDep = GetVirtualTime();
                map->state[j] = MAP_STATE_WORKER;
            }
        }

        if (!worker->moving && IsCoordEqual(worker->position, worker->destiny) && worker->curTask)
        {
            COORD accessor = AddCoord(worker->curTask->object->position, worker->curTask->object->data->accessor);
            if (IsCoordEqual(worker->curTask->object->position, accessor))
            {
                // Objet multidirectionnel, voir GetObjectAccessor()
            }
            else
            {
                if (map->state[accessor.Y * map->width + accessor.X] == MAP_STATE_FREE)
                    worker->destiny = accessor;
            }
        }
    }
}

Worker* GetWorkerByPosition(COORD coord)
{
    return GetWorkerByPositionXY(coord.X, coord.Y);
}
Worker* GetWorkerByPositionXY(int x, int y)
{
    int i;
    for (i = 0; i < workerCount; i++)
        if (workerList[i]->position.X == x && workerList[i]->position.Y == y)
            return workerList[i];
    return NULL;
}

int GetWorkerBmpId(Worker* worker)
{
    return (((worker->position.X + worker->position.Y) % 2) + 1) * ((((worker->moving ? GetVirtualTime() - worker->lastDep : 0) * 16 / TIME_PER_CASE) / 8) % 2) * 4 + worker->dir;
}
int GetWorkerStep(Worker* worker)
{
    int i = worker->moving ? GetVirtualTime() - worker->lastDep : 0;
    i = i * 16 / TIME_PER_CASE;
    return i > 15 ? 15 : i;
}
short GetWorkerCoordX(Worker* worker)
{
    if (worker->dir == DIR_LEFT)
        return worker->position.X * 16 - GetWorkerStep(worker);
    else if (worker->dir == DIR_RIGHT)
        return worker->position.X * 16 + GetWorkerStep(worker);
    else
        return worker->position.X * 16;
}
short GetWorkerCoordY(Worker* worker)
{
    if (worker->dir == DIR_UP)
        return worker->position.Y * 16 - GetWorkerStep(worker) - 4;
    else if (worker->dir == DIR_DOWN)
        return worker->position.Y * 16 + GetWorkerStep(worker) - 4;
    else
        return worker->position.Y * 16 - 4;
}




int WorkerComparer(const void* worker1, const void* worker2)
{
	return ((Worker*)worker2)->position.Y - ((Worker*)worker1)->position.Y;
}


void SortWorkers()
{
    qsort(workerList, workerCount, sizeof(Worker* ), WorkerComparer);
}


void AddNewTask(Worker* worker, Object* object, ObjectAction* action)
{
    Task *newTask, *lastTask;

    newTask = (Task*)malloc(sizeof(Task));
    newTask->object = object;
    newTask->action = action;
    newTask->nextTask = NULL;
    newTask->totalTime = -1;

    if (!worker->curTask)
        worker->curTask = newTask;
    else
    {
        lastTask = worker->curTask;
        while (lastTask && lastTask->nextTask)
            lastTask = lastTask->nextTask;
        lastTask->nextTask = newTask;
    }
}

void DelTask(Worker* worker, Task* task)
{
    Task *curTask, *nextTask;

    if (worker->curTask == task)
    {
        nextTask = worker->curTask->nextTask;
        free(worker->curTask);
        worker->curTask = nextTask;
    }
    else if (worker->curTask)
    {
        curTask = worker->curTask;
        while (curTask->nextTask)
        {
            if (curTask->nextTask == task)
            {
                nextTask = curTask->nextTask->nextTask;
                free(curTask->nextTask);
                curTask->nextTask = nextTask;
            }
            curTask = curTask->nextTask;
        }
    }
}

void ManageTasks()
{
    int i;

    Lock(workerMutex);
    for (i = 0; i < workerCount; i++)
    {
        Worker* worker = workerList[i];
        if (!worker->moving && IsCoordEqual(worker->position, worker->destiny) && worker->curTask && IsObjectAccessor(worker->position, worker->curTask->object))
        {
            if (!worker->curTask->object->busy)
            {
                if (worker->curTask->object->data->accessor.X >= worker->curTask->object->data->width)
                    worker->dir = DIR_LEFT;
                if (worker->curTask->object->data->accessor.X < 0)
                    worker->dir = DIR_RIGHT;
                if (worker->curTask->object->data->accessor.Y >= worker->curTask->object->data->height)
                    worker->dir = DIR_UP;
                if (worker->curTask->object->data->accessor.Y < 0)
                    worker->dir = DIR_DOWN;

                worker->curTask->object->busy = true;
                if (worker->curTask->totalTime < 0)
                    worker->curTask->totalTime = 0;
                worker->curTask->lastTime = GetVirtualTime();
                worker->workingObject = worker->curTask->object;
            }
            else if (worker->workingObject == worker->curTask->object)
            {
                int curTime = GetVirtualTime();
                worker->curTask->totalTime += curTime - worker->curTask->lastTime;
                worker->curTask->lastTime = curTime;

                if (worker->curTask->action->duration >= 0 && worker->curTask->totalTime >= worker->curTask->action->duration)
                {
                    worker->experience += worker->curTask->action->experience;
                    worker->curTask->object->usury += worker->curTask->action->usury;
                    worker->curTask->object->busy = false;
                    worker->workingObject = NULL;
                    DelTask(worker, worker->curTask);
                }
            }
        }
        else if (worker->curTask && worker->workingObject == worker->curTask->object)
        {
            worker->curTask->object->busy = false;
            worker->workingObject = NULL;
        }
    }
    UnLock(workerMutex);
}